---
id: jsonp
title: 什么是 JSONP 请求
---

import Thumbnail from "@site/src/components/Thumbnail";

## 前言

这上一个很老的话题了，还是要搬出来讲讲，就当复习一下吧

## 何为 JSONP

JSONP 是`JSON with Padding`的略称，JSONP 是一种跨域解决方案，通过客户端的`<script>`标签发出的请求方式。

那请求何必做得如此麻烦，直接使用 ajax 做请求岂不美哉，这里便要涉及到一个同源和跨域的问题，往下。

## URL

URL 代表着是统一资源定位符（Uniform Resource Locator）。URL 无非就是一个给定的独特资源在 Web 上的地址。理论上说，每个有效的 URL 都指向一个唯一的资源。这个资源可以是一个 HTML 页面，一个 CSS 文档，一幅图像，等等。而在实际中，也有一些例外，最常见的情况就是一个 URL 指向了不存在的或是被移动过的资源。由于通过 URL 呈现的资源和 URL 本身由 Web 服务器处理，因此 web 服务器的拥有者需要认真地维护资源以及与它关联的 URL

### URL 的示例

```http
https://developer.mozilla.org
https://developer.mozilla.org/en-US/docs/Learn/
https://developer.mozilla.org/en-US/search?q=URL
```

### URL 组成

一个 URL 由不同的部分组成，其中一些是必须的，而另一些是可选的。让我们以下面这个 URL 为例看看其中最重要的部分：

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-all.png)

#### 协议

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-protocol@x2_update.png)

`http` 是协议。它表明了浏览器必须使用何种协议。它通常都是 HTTP 协议或是 HTTP 协议的安全版，即 HTTPS。Web 需要它们二者之一，但浏览器也知道如何处理其他协议，比如 `mailto`:（打开邮件客户端）或者 `ftp`:（处理文件传输），所以当你看到这些协议时，不必惊讶。

### 域名

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-authority.png)

域名，它通过`://`符号来跟协议分隔

`www.example.com` 是域名。它表明正在请求哪个 Web 服务器。或者，可以直接使用`IP address`, 但是因为它不太方便，所以它不经常在网络上使用。

`:80`表示端口号,它表示用于访问 Web 服务器上的资源的技术“门”。如果 Web 服务器使用 HTTP 协议的标准端口（HTTP 为 80，HTTPS 为 443）来授予其资源的访问权限，则通常会被忽略。否则是强制性的。

### 资源路径

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-path@x2.png)

`/path/to/myfile.html` 是网络服务器上资源的路径。在 Web 的早期阶段，像这样的路径表示 Web 服务器上的物理文件位置。如今，它主要是由没有任何物理现实的 Web 服务器处理的抽象。

### 查询参数

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-parameters@x2.png)

`?key1=value1&key2=value2` 是提供给网络服务器的额外参数。这些参数是用 `&` 符号分隔的键/值对列表。在返回资源之前，Web 服务器可以使用这些参数来执行额外的操作。每个 Web 服务器都有自己关于参数的规则，唯一可靠的方式来知道特定 Web 服务器是否处理参数是通过询问 Web 服务器所有者。

### hash

![url](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL/mdn-url-anchor@x2.png)

`#SomewhereInTheDocument` 是资源本身的另一部分的锚点。锚点表示资源中的一种“书签”，给浏览器显示位于该“加书签”位置的内容的方向。例如，在 HTML 文档上，浏览器将滚动到定义锚点的位置;在视频或音频文档上，浏览器将尝试转到锚代表的时间。值得注意的是，#后面的部分，也称为片段标识符，永远不会随请求发送到服务器。

## 同源请求和跨域请求

同源策略，它是由 Netscape 网景公司提出的一个著名的安全策略。

现在所有支持 JavaScript 的`浏览器`都会使用这个策略。所谓同源需要需足以下三个条件

1. 协议(protocol)相同: 同为 https 或 http
2. 域名相同
3. 端口相同: 如未指定，http 默认为 80 端口，https 默认为 443 端口

而所有非同源的请求（即 `域名`，`协议`，`端口` 其中一种或多种不相同），都会被作为跨域请求，浏览器会将其非同源的响应数据丢弃。

这里可以理解为是浏览器在搞事情，服务端确确实实有返回数据，浏览器接收到返回的数据，发现我们请求的是一个非同源的数据，浏览器再将其响应报文丢弃掉

而通过一些标签发出的请求则不会被进行同源检查，比如`<script>`标签，`<img/>`标签等等，本文讲述`JSONP`便是通过`<script>`标签做的请求。

## JSONP 的实现流程

<Thumbnail
  src="/myimage/jsonp1.png"
  alt="Choose either AWS or GCP"
  width="556px"
  height="400px"
/>

好了，现在我们按照流程图，来一步步用代码实现

### 在发请求先，准备一个全局的接收函数

```js
//声明一个全局函数 'callback'，用于接收响应数据
window.myCallback = (res) => {
  console.log(res);
};
```

### 在 html 创建 script 标签，发出请求

```html
<html>
.......
<script>
 window.myCallback = (res)=>{   //这里为上一步定义的全局函数
  console.log(res)
 }
</script>
<script url="xxx?callback=myCallback">
   //script标签的请求必须在写在定义全局函数之后
   //这里需将全局函数的函数名作为参数callback的value传递
   //这里callback这个键名是前后端约定好的
</script>
</body>
</html>
```

### 服务端接收到请求，将如下数据相应回

```js
myCallback({
  //一个函数的调用，将数据作为参数传递进去，再将整个函数的调用返回给客户端
  name: "ahreal",
  age: 18
});
```

### 客户端接收到服务端的相应，相当于：

```html
<html>
....
<script>
 window.myCallback = (res)=>{   //这里为上一步定义的全局函数
  console.log(res)
 }
</script>
<script>       //将接收到的数据作为script标签里面的内容展开执行
    myCallback({
        name:'ahreal',
        age:18
    })
</script>
</body>
</html>
```

